package com.rankeiot.platform.service;

import lombok.Data;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 服务器端在线消息推送,注意消息体不要太大
 */
@Component
public class ServerEventPushService {
    @Data
    public static class ServerEvent{
        private String event;
        private Object data;
    }
    private static Map<String,Map<String,SseEmitter>> subscribeMap=new ConcurrentHashMap<>();
    public synchronized SseEmitter subscribe(String username,String uid){
        Map<String,SseEmitter> emitterMap=subscribeMap.get(username);
        if(emitterMap==null){
            emitterMap=new ConcurrentHashMap<>();
            subscribeMap.put(username,emitterMap);
        }
        SseEmitter sseEmitter=emitterMap.get(uid);
        if(sseEmitter==null){
            sseEmitter=new SseEmitter(0L);
            sseEmitter.onTimeout(()->{
                remove(username,uid);
            });
            sseEmitter.onError(e->{
                remove(username,uid);
            });
            sseEmitter.onCompletion(()->{
                remove(username,uid);
            });
            emitterMap.put(uid,sseEmitter);
        }
        return sseEmitter;
    }
    private synchronized void remove(String username,String uid){
        Map<String,SseEmitter> emitterMap=subscribeMap.get(username);
        if(emitterMap!=null){
            SseEmitter sseEmitter=emitterMap.remove(uid);
            if(sseEmitter!=null){
                sseEmitter.complete();
                if(emitterMap.isEmpty()){
                    subscribeMap.remove(username);
                }
            }
        }
    }
    public void sendEvent(String username,String event,Object data){
        ServerEvent serverEvent = new ServerEvent();
        serverEvent.setEvent(event);
        serverEvent.setData(data);
         sendEvent(username,serverEvent);
    }
    public void sendEvent(String username,ServerEvent event){
        Map<String,SseEmitter> emitterMap=subscribeMap.get(username);
        if(emitterMap==null){
            return;
        }
        for(SseEmitter sseEmitter:emitterMap.values()){
            try{
                sseEmitter.send(event, MediaType.APPLICATION_JSON);
            }catch (IOException e){
                sseEmitter.complete();
                subscribeMap.remove(username);
                e.printStackTrace();
            }
        }
    }
    public void sendAll(String event,Object data){
        ServerEvent serverEvent = new ServerEvent();
        serverEvent.setEvent(event);
        serverEvent.setData(data);
        sendAll(serverEvent);
    }
    public void sendAll(ServerEvent event){
        for (String key : subscribeMap.keySet()) {
           sendEvent(key,event);
        }
    }
    @Async("threadPoolTaskExecutor")
    @Scheduled(fixedDelay = 1000*60) // 1min
    public void checkAlive() {
        for(Map.Entry<String,Map<String,SseEmitter>> emitterEntry: subscribeMap.entrySet()){
            Map<String,SseEmitter> emitterMap = emitterEntry.getValue();
            for(SseEmitter emitter:emitterMap.values()){
                try {
                    emitter.send("P");
                } catch (IOException e) {
                    emitter.complete();
                }
            }

        }
    }
}
